package com.xiaopeng.common.excel;


import cn.hutool.core.bean.BeanUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.annotation.write.style.ColumnWidth;
import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.metadata.data.WriteCellData;
import com.alibaba.excel.metadata.property.ExcelContentProperty;
import com.alibaba.excel.write.builder.ExcelWriterBuilder;
import com.alibaba.excel.write.handler.CellWriteHandler;
import com.alibaba.excel.write.handler.context.CellWriteHandlerContext;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.metadata.style.WriteFont;
import com.alibaba.fastjson2.JSON;
import com.xiaopeng.common.enumUtil.EnumView;
import com.xiaopeng.common.exception.BusinessException;
import com.xiaopeng.common.exception.util.Assert;
import com.xiaopeng.common.page.BasePageRequest;
import com.xiaopeng.common.page.PageVO;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFCellStyle;
import org.apache.poi.xssf.usermodel.XSSFColor;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.net.URLEncoder;
import java.util.*;
import java.util.function.Consumer;
import java.util.function.Function;

/**
 * Excel导出工具类
 * 基于EasyExcel 对分页接口结果集进行Excel导出
 * 优点：提高接口复用， 分批次查询 减轻数据库单次压力
 *
 * @author xiaopeng
 */
@Slf4j
public class ExcelExportUtil {

    // 导出类型：HttpServletResponse 方式导出
    private static final int EXPORT_TYPE_HTTP_RESPONSE = 1;
    // 导出类型：本地文件 方式导出
    private static final int EXPORT_TYPE_FILE = 2;
    // 导出类型：输出流方式
    private static final int EXPORT_TYPE_OUTPUT_STREAM = 3;

    /**
     * HttpServletResponse 方式导出
     * @param fileName  文件名
     * @return
     */
    public static ExcelExportHandler httpResponse(String fileName) {
        ExcelExportHandler excelExportHandler = new ExcelExportHandler(EXPORT_TYPE_HTTP_RESPONSE);
        excelExportHandler.fileName = fileName;
        return excelExportHandler;
    }

    /**
     * 本地文件 方式导出
     * @param filePath 文件路径地址
     * @return
     */
    public static ExcelExportHandler file(String filePath) {
        return file(new File(filePath));
    }

    /**
     * 本地文件 方式导出
     * @param file 文件file
     * @return
     */
    public static ExcelExportHandler file(File file) {
        ExcelExportHandler excelExportHandler = new ExcelExportHandler(EXPORT_TYPE_FILE);
        String fileName = file.getName();
        excelExportHandler.fileName = fileName.substring(0, fileName.indexOf('.'));
        excelExportHandler.file = file;
        return excelExportHandler;
    }

    /**
     * 输出流方式
     * @param outputStream 输出流
     * @return
     */
    public static ExcelExportHandler outputStream(OutputStream outputStream) {
        ExcelExportHandler excelExportHandler = new ExcelExportHandler(EXPORT_TYPE_OUTPUT_STREAM);
        excelExportHandler.outputStream = outputStream;
        return excelExportHandler;
    }


    public static  class DataCollectHandler<RE extends BasePageRequest, RS> {

        /**
         * sheet名字
         */
        private String sheetName;

        /**
         * 数据源：列表数据
         */
        private List<RS> dataList;

        /**
         * 数据源：分页请求
         */
        private Function<RE, PageVO<RS>> pageApi;

        /**
         * 分页参数
         */
        private RE pageParams;

        /**
         * 视图映射：class
         */
        private Class<?> clazz;

        /**
         * 视图映射:动态列map映射
         */
        private ExcelDynamicColumn<RS> dynamicColumn;

    }

    public static class ExcelExportHandler {

        /**
         * 导出方式
         */
        private int exportType;

        /**
         *  文件名
         */
        private String fileName = null;


        /**
         *  本地文件方式导出
         */
        private File file = null;

        /**
         *  输出流方式导出
         */
        private OutputStream outputStream = null;

        /**
         * 分页请求 - Page size
         */
        private int pageSize = 1000;

        /**
         * 分页请求 - 表格导出允许最大数量限制 -1不限制
         */
        private int maxTotalRows = 10000;

        /**
         * 分页请求 - 总条数超过maxTotalRows提示语，
         * maxTotalRows   最大数量限制
         * totalRows     分页请求总条数
        */
        private String overMaxTotalRowsLimitErrMsg = "单词可导出最大数量为${maxTotalRows}，当前搜索条件查询数量为{totalRows}，请调整搜索条件";

        /**
         * 数据源
         */
        private List<DataCollectHandler> dataCollectHandlerList = new ArrayList<>();

        /**
         * excelWriterBuilder 进行操作修改
         */
        private Consumer<ExcelWriterBuilder> excelWriterConfig;


        /**
         * 表头单元格样式处理
         */
        private Consumer<CellWriteHandlerContext> headCellStyleConfig;

        /**
         * 内容单元格样式处理
         */
        private Consumer<CellWriteHandlerContext> contentCellStyleConfig;

        public ExcelExportHandler(int exportType) {
            this.exportType = exportType;
        }

        /**
         * 设置文件名
         * @param fileName 文件名
         * @return
         */
        public ExcelExportHandler pageSize(String fileName) {
            this.fileName = fileName;
            return this;
        }

        /**
         * 设置分页参数
         * @param pageSize          分页请求 - Page size
         * @return
         */
        public ExcelExportHandler pageSize(int pageSize) {
            this.pageSize = pageSize;
            return this;
        }

        /**
         * 设置分页参数
         * @param maxTotalRows      分页请求 - 表格导出允许最大数量限制 -1不限制
         * @return
         */
        public ExcelExportHandler maxTotalRows(int maxTotalRows) {
            this.maxTotalRows = maxTotalRows;
            return this;
        }

        /**
         * 设置分页参数
         * @param overMaxTotalRowsLimitErrMsg    分页请求 - 表格导出允许最大数量限制 -1不限制
         * @return
         */
        public ExcelExportHandler overMaxTotalRowsLimitErrMsg(String overMaxTotalRowsLimitErrMsg) {
            this.overMaxTotalRowsLimitErrMsg = overMaxTotalRowsLimitErrMsg;
            return this;
        }

        /**
         * 设置分页参数
         * @param pageSize          分页请求 - Page size
         * @param maxTotalRows      分页请求 - 表格导出允许最大数量限制 -1不限制
         * @return
         */
        public ExcelExportHandler pageParams(int pageSize, int maxTotalRows) {
            this.pageSize = pageSize;
            this.maxTotalRows = maxTotalRows;
            return this;
        }

        /**
         * 设置分页参数
         * @param maxTotalRows      分页请求 - 表格导出允许最大数量限制 -1不限制
         * @param overMaxTotalRowsLimitErrMsg    分页请求 - 表格导出允许最大数量限制 -1不限制
         * @return
         */
        public ExcelExportHandler pageParams(int maxTotalRows, String overMaxTotalRowsLimitErrMsg) {
            this.maxTotalRows = maxTotalRows;
            this.overMaxTotalRowsLimitErrMsg = overMaxTotalRowsLimitErrMsg;
            return this;
        }

        /**
         * 设置分页参数
         * @param pageSize          分页请求 - Page size
         * @param maxTotalRows      分页请求 - 表格导出允许最大数量限制 -1不限制
         * @param overMaxTotalRowsLimitErrMsg    分页请求 - 表格导出允许最大数量限制 -1不限制
         * @return
         */
        public ExcelExportHandler pageParams(int pageSize, int maxTotalRows, String overMaxTotalRowsLimitErrMsg) {
            this.pageSize = pageSize;
            this.maxTotalRows = maxTotalRows;
            this.overMaxTotalRowsLimitErrMsg = overMaxTotalRowsLimitErrMsg;
            return this;
        }

        /**
         * 添加sheet页 - 列表导出（固定列）
         *
         * @param sheetName sheet页名称
         * @param dataList  数据源：列表数据
         * @param clazz     视图映射：class
         * @param <RS>      导出数据类型
         */
        public <RS> ExcelExportHandler addSheet(String sheetName, List<RS> dataList, Class<?> clazz) {
            DataCollectHandler<BasePageRequest, RS> dataCollectHandler = new DataCollectHandler<>();
            dataCollectHandler.sheetName = sheetName;
            dataCollectHandler.dataList = dataList;
            dataCollectHandler.clazz = clazz;
            dataCollectHandlerList.add(dataCollectHandler);
            return this;
        }

        /**
         * 添加sheet页 - 列表导出（动态列）
         *
         * @param sheetName     sheet页名称
         * @param dataList      数据源：列表数据
         * @param dynamicColumn 视图映射:动态列map映射
         * @param <RS>          导出数据类型
         */
        public <RS> ExcelExportHandler addSheet(String sheetName, List<RS> dataList, ExcelDynamicColumn<RS> dynamicColumn) {
            DataCollectHandler<BasePageRequest, RS> dataCollectHandler = new DataCollectHandler<>();
            dataCollectHandler.sheetName = sheetName;
            dataCollectHandler.dataList = dataList;
            dataCollectHandler.dynamicColumn = dynamicColumn;
            dataCollectHandlerList.add(dataCollectHandler);
            return this;
        }

        /**
         * 添加sheet页 - 分页导出（固定列）
         *
         * @param sheetName  sheet页名称
         * @param pageParams 分页参数
         * @param pageApi    分页接口
         * @param clazz      视图映射：class
         * @return
         * @param <RE>       分页请求参数类型
         * @param <RS>       导出数据类型
         */
        public <RE extends BasePageRequest, RS> ExcelExportHandler addSheet(String sheetName, RE pageParams, Function<RE, PageVO<RS>> pageApi, Class<?> clazz) {
            DataCollectHandler<RE, RS> dataCollectHandler = new DataCollectHandler<>();
            dataCollectHandler.sheetName = sheetName;
            dataCollectHandler.pageParams = pageParams;
            dataCollectHandler.pageApi = pageApi;
            dataCollectHandler.clazz = clazz;
            dataCollectHandlerList.add(dataCollectHandler);
            return this;
        }

        /**
         * 添加sheet页 - 分页导出（动态列）
         *
         * @param sheetName  sheet页名称
         * @param pageParams 分页参数
         * @param pageApi    分页接口
         * @param dynamicColumn 视图映射:动态列map映射
         * @return
         * @param <RE>       分页请求参数类型
         * @param <RS>       导出数据类型
         */
        public <RE extends BasePageRequest, RS> ExcelExportHandler addSheet(String sheetName, RE pageParams, Function<RE, PageVO<RS>> pageApi, ExcelDynamicColumn<RS> dynamicColumn) {
            DataCollectHandler<RE, RS> dataCollectHandler = new DataCollectHandler<>();
            dataCollectHandler.sheetName = sheetName;
            dataCollectHandler.pageParams = pageParams;
            dataCollectHandler.pageApi = pageApi;
            dataCollectHandler.dynamicColumn = dynamicColumn;
            dataCollectHandlerList.add(dataCollectHandler);
            return this;
        }

        /**
         * 表头单元格样式处理
         * @param headCellStyleConfig
         * @return
         */
        public ExcelExportHandler headCellStyleConfig(Consumer<CellWriteHandlerContext> headCellStyleConfig) {
            this.headCellStyleConfig = headCellStyleConfig;
            return this;
        }


        /**
         * 内容单元格样式处理
         * @param contentCellStyleConfig
         * @return
         */
        public ExcelExportHandler contentCellStyleConfig(Consumer<CellWriteHandlerContext> contentCellStyleConfig) {
            this.contentCellStyleConfig = contentCellStyleConfig;
            return this;
        }


        /**
         * 进行操作配置修改
         * @param excelWriterConfig
         * @return
         */
        public ExcelExportHandler excelWriterConfig(Consumer<ExcelWriterBuilder> excelWriterConfig) {
            this.excelWriterConfig = excelWriterConfig;
            return this;
        }


        /**
         * 完善ExcelWriterBuilder信息 并构建ExcelWriter
         */
        private ExcelWriter getExcelWriter(ExcelWriterBuilder excelWriterBuilder) {
            // 自定义样式处理
            excelWriterBuilder.registerWriteHandler(new StyleWriteHandler(headCellStyleConfig, contentCellStyleConfig));
            // 枚举值映射转换
            excelWriterBuilder.registerWriteHandler(new ExcelEnumWriteHandler());
            // 如果存在自定义配置 进行配置
            if (Objects.nonNull(excelWriterConfig)) {
                excelWriterConfig.accept(excelWriterBuilder);
            }
            return excelWriterBuilder.build();
        }

        /**
         * 处理HttpServletResponse导出
         */
        private void handleHttpResponse() {
            ServletRequestAttributes servletRequestAttributes =  (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            Assert.notNull(servletRequestAttributes, "HttpServletResponse获取失败");
            HttpServletResponse response = servletRequestAttributes.getResponse();
            try {
                response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
                response.setCharacterEncoding("utf-8");
                // 这里URLEncoder.encode可以防止中文乱码
                String fileNameEncode = URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", "%20");
                response.setHeader("Content-disposition", "attachment;filename*=" + fileNameEncode + ".xlsx");
                // 这里需要设置不关闭流
                ExcelWriterBuilder excelWriterBuilder = EasyExcel.write(response.getOutputStream()).autoCloseStream(Boolean.FALSE);
                ExcelWriter excelWriter = getExcelWriter(excelWriterBuilder);
                // 写入数据
                this.handleDataWriting(excelWriter);
            } catch (Exception e) {
                log.error("文件导出异常 文件类型：" + fileName, e);
                try {
                    // 重置response
                    response.reset();
                    response.setContentType("application/json");
                    response.setCharacterEncoding("utf-8");
                    Map<String, Object> map = new HashMap<>();
                    map.put("code", 500);
                    map.put("success", false);
                    map.put("message", e.getMessage());
                    response.getWriter().println(JSON.toJSONString(map));
                } catch (IOException ex) {
                    throw new RuntimeException(ex);
                }
            }
        }


        /**
         * 本地文件 方式导出
         */
        private void handleFile() {
            ExcelWriterBuilder excelWriterBuilder = EasyExcel.write(file);
            ExcelWriter excelWriter = getExcelWriter(excelWriterBuilder);
            this.handleDataWriting(excelWriter);
        }


        /**
         * 输出流 方式导出
         */
        private void handleOutputStream() {
            ExcelWriterBuilder excelWriterBuilder = EasyExcel.write(outputStream);
            ExcelWriter excelWriter = getExcelWriter(excelWriterBuilder);
            this.handleDataWriting(excelWriter);
        }


        /**
         * 数据写入
         * @param excelWriter
         */
        private void handleDataWriting(ExcelWriter excelWriter) {
            int sheetNo = 0;
            try {
                for (; sheetNo < dataCollectHandlerList.size(); sheetNo++) {
                    DataCollectHandler dataCollectHandler = dataCollectHandlerList.get(sheetNo);
                    WriteSheet writeSheet;
                    if (Objects.nonNull(dataCollectHandler.clazz)) {
                        // 固定列 class定义表头
                        writeSheet = EasyExcel.writerSheet(sheetNo, dataCollectHandler.sheetName).head(dataCollectHandler.clazz).build();
                    }  else if (Objects.nonNull(dataCollectHandler.dynamicColumn)) {
                        // 动态列 map定义表头
                        writeSheet = EasyExcel.writerSheet(sheetNo, dataCollectHandler.sheetName).head(dataCollectHandler.dynamicColumn.head()).build();
                    } else {
                        throw BusinessException.exception("缺失视图映射");
                    }

                    if (Objects.nonNull(dataCollectHandler.dataList)) {
                        // 列表数据直接导出
                        writeSheetData(excelWriter, writeSheet,dataCollectHandler.dataList, dataCollectHandler.clazz, dataCollectHandler.dynamicColumn);
                    } else {
                        // 分页请求 分批导出
                        int currentPageNum = 1;
                        PageVO page = null;
                        do {
                            // 获取分页数据
                            page = queryPage(dataCollectHandler.pageApi, dataCollectHandler.pageParams, currentPageNum, pageSize);
                            // 加油最大导出数量
                            verifyPageTotalNum(page.getTotalRows());
                            // 写入sheet
                            writeSheetData(excelWriter, writeSheet, page.getRoot(), dataCollectHandler.clazz, dataCollectHandler.dynamicColumn);
                            currentPageNum++;
                        } while (currentPageNum <= page.getTotalPages());
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
                throw BusinessException.exception("sheet-{} 导出异常 {}", sheetNo, e.getMessage());
            }
            excelWriter.finish();
        }

        /**
         * 请求服务层接口 返回分页page
         *
         * @param pageApi       请求接口-返回总数和list
         * @param reqDTO        分页请求入参
         * @param pageNum       分页第几页
         * @param pageSize      每次请求分页数量
         */
        private <RE extends BasePageRequest, RS> PageVO<RS> queryPage(
                Function<RE, PageVO<RS>> pageApi, RE reqDTO, int pageNum, int pageSize) {
            reqDTO.setPageNum(pageNum);
            reqDTO.setPageSize(pageSize);
            reqDTO.setIsCount(pageNum == 1);
            return pageApi.apply(reqDTO);
        }


        /**
         * 校验导出条数是否超过最大数量限制
         * @param totalRows
         * @param <RS>
         */
        private <RS> void verifyPageTotalNum(Long totalRows) {
            // -1不限制
            if (maxTotalRows == -1) {
                return;
            }
            Assert.notNull(totalRows, "page总条数为空");
            if (totalRows > maxTotalRows) {
                overMaxTotalRowsLimitErrMsg = overMaxTotalRowsLimitErrMsg.replace("${maxTotalRows}", String.valueOf(maxTotalRows));
                overMaxTotalRowsLimitErrMsg = overMaxTotalRowsLimitErrMsg.replace("${totalRows}", String.valueOf(totalRows));
                throw BusinessException.exception(overMaxTotalRowsLimitErrMsg);
            }
        }

        /**
         * 写入数据到sheet
         * @param excelWriter
         * @param writeSheet
         * @param dataList
         * @param clazz
         * @param dynamicColumn
         * @param <RE>
         * @param <RS>
         */
        private <RE extends BasePageRequest, RS> void writeSheetData(
                ExcelWriter excelWriter, WriteSheet writeSheet, List<RS> dataList,  Class<?> clazz,  ExcelDynamicColumn<RS> dynamicColumn) {
            if (CollectionUtils.isEmpty(dataList)) {
                return;
            }
            if (Objects.nonNull(clazz)) {
                List<?> excelVOList = dataList;
                // 如果数据集合和excel导出类不是同一个 则进行转换
                if (dataList.getFirst().getClass() != clazz) {
                    excelVOList = BeanUtil.copyToList(dataList, clazz);
                }
                excelWriter.write(excelVOList, writeSheet);
            } else if (Objects.nonNull(dynamicColumn)) {
                List<List<Object>> columnDataList = new ArrayList<>(dataList.size());
                for (RS res : dataList) {
                    columnDataList.add(dynamicColumn.columnConvert(res));
                }
                excelWriter.write(columnDataList, writeSheet);
            } else {
                throw BusinessException.exception("缺失视图映射");
            }
        }

        /**
         * 执行导出
         */
        public void execute() {
            if (exportType == EXPORT_TYPE_HTTP_RESPONSE) {
                // HttpServletResponse 方式导出
                this.handleHttpResponse();
            } else if (exportType == EXPORT_TYPE_FILE) {
                // 本地文件 方式导出
                this.handleFile();
            }  else if (exportType == EXPORT_TYPE_OUTPUT_STREAM) {
                // 输出流方式导出
                this.handleOutputStream();
            } else {
                throw new RuntimeException("导出类型无效");
            }
        }

        /**
         * 添加sheet页 并执行导出 （用于下载导入模板）
         * @param clazz     视图映射：class
         */
        public <RS> void execute(Class<?> clazz) {
            this.addSheet(fileName, Collections.emptyList(), clazz);
            execute();
        }

        /**
         * 添加sheet页 并执行导出
         * @param dataList  数据源：列表数据
         * @param clazz     视图映射：class
         */
        public <RS> void execute(List<RS> dataList, Class<?> clazz) {
            this.addSheet(fileName, dataList, clazz);
            execute();
        }

        /**
         * 添加sheet页 并执行导出
         *
         * @param dataList      数据源：列表数据
         * @param dynamicColumn 视图映射:动态列map映射
         * @param <RS>          导出数据类型
         */
        public <RS> void execute( List<RS> dataList, ExcelDynamicColumn<RS> dynamicColumn) {
            this.addSheet(fileName, dataList, dynamicColumn);
            execute();
        }

        /**
         * 添加sheet页 并执行导出
         *
         * @param pageParams 分页参数
         * @param pageApi    分页接口
         * @param clazz      视图映射：class
         * @return
         * @param <RE>       分页请求参数类型
         * @param <RS>       导出数据类型
         */
        public <RE extends BasePageRequest, RS> void execute(RE pageParams, Function<RE, PageVO<RS>> pageApi, Class<?> clazz) {
            this.addSheet(fileName, pageParams, pageApi, clazz);
            execute();
        }

        /**
         * 添加sheet页 - 分页导出（动态列）
         *
         * @param pageParams 分页参数
         * @param pageApi    分页接口
         * @param dynamicColumn 视图映射:动态列map映射
         * @return
         * @param <RE>       分页请求参数类型
         * @param <RS>       导出数据类型
         */
        public <RE extends BasePageRequest, RS> void execute(RE pageParams, Function<RE, PageVO<RS>> pageApi, ExcelDynamicColumn<RS> dynamicColumn) {
            this.addSheet(fileName, pageParams, pageApi, dynamicColumn);
            execute();
        }
    }



    /**
     * Excel 自定义样式处理拦截器
     */
    public static class StyleWriteHandler implements CellWriteHandler {


        /**
         * 表头单元格样式处理
         */
        private Consumer<CellWriteHandlerContext> headCellStyleConfig;

        /**
         * 内容单元格样式处理
         */
        private Consumer<CellWriteHandlerContext> contentCellStyleConfig;

        public StyleWriteHandler() { }

        public StyleWriteHandler(Consumer<CellWriteHandlerContext> headCellStyleConfig, Consumer<CellWriteHandlerContext> contentCellStyleConfig) {
            this.headCellStyleConfig = headCellStyleConfig;
            this.contentCellStyleConfig = contentCellStyleConfig;
        }

        @Override
        public void afterCellDispose(CellWriteHandlerContext context) {
            // 该方法在接口中最后执行，可对样式进行最终修改
            // 参考：HorizontalCellStyleStrategy
            if (context.getHead() == null) {
                return;
            }
            if (context.getHead()) {
                setHeadCellStyle(context);
            } else {
                setContentCellStyle(context);
            }
        }

        /**
         * 处理表头样式
         * @param context
         */
        private void setHeadCellStyle(CellWriteHandlerContext context) {
            Cell cell = context.getCell();
            Head headData = context.getHeadData();
            Sheet sheet = context.getWriteSheetHolder().getSheet();
            Workbook workbook = context.getWriteWorkbookHolder().getWorkbook();
            CellStyle cellStyle = workbook.createCellStyle();
            cellStyle.cloneStyleFrom(cell.getCellStyle());

            // 设置行高
            Row row = cell.getRow();
            row.setHeightInPoints(24);  // 设置高度多少磅（Point）

            // 设置列宽
            Field field = headData.getField();
            ColumnWidth columnWidth = field.getAnnotation(ColumnWidth.class);
            if (columnWidth == null) {
                sheet.setColumnWidth(cell.getColumnIndex(), Math.min((cell.getStringCellValue().getBytes().length + 3) * 256, 20 * 256));
            }

            // 冻结列 固定展示
            sheet.createFreezePane(0, 1);

            // 设置表头rgb背颜色
            if (cellStyle instanceof XSSFCellStyle xssfCellColorStyle) {
                byte[] rgb = new byte[]{(byte) 24, (byte) 112, (byte) 255};
                xssfCellColorStyle.setFillForegroundColor(new XSSFColor(rgb, null));
                // 这里需要指定 FillPatternType 为FillPatternType.SOLID_FOREGROUND
                cellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
            }

            // 设置水平对齐方式为居中对齐
            cellStyle.setAlignment(HorizontalAlignment.CENTER);
            // 设置垂直对齐方式为居中对齐
            cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);

            // 边框样式
            cellStyle.setLeftBorderColor(IndexedColors.WHITE.getIndex());
            cellStyle.setRightBorderColor(IndexedColors.WHITE.getIndex());
            cellStyle.setBorderLeft(BorderStyle.THIN);
            cellStyle.setBorderRight(BorderStyle.THIN);
            cellStyle.setBorderTop(BorderStyle.THIN);
            cellStyle.setBorderBottom(BorderStyle.THIN);

            // 设置字体样式
            Font font = workbook.createFont();
            font.setFontName("微软雅黑");
            // font.setBold(true);
            font.setFontHeightInPoints((short) 11);
            font.setColor(IndexedColors.WHITE.getIndex());
            cellStyle.setFont(font);

            cell.setCellStyle(cellStyle);
            if(Objects.nonNull(headCellStyleConfig)) {
                headCellStyleConfig.accept(context);
            }
            // 这里要把 WriteCellData的样式清空， 不然后面还有一个拦截器 FillStyleCellWriteHandler 默认会将 WriteCellStyle 设置到
            // cell里面去 会导致自己设置的不一样（很关键）
            context.getFirstCellData().setWriteCellStyle(null);
        }

        /**
         * 处理数据内容样式
         * @param context
         */
        private void setContentCellStyle(CellWriteHandlerContext context) {
            Cell cell = context.getCell();
            Workbook workbook = context.getWriteWorkbookHolder().getWorkbook();
            CellStyle cellStyle = workbook.createCellStyle();

            // 设置行高
            Row row = cell.getRow();
            row.setHeightInPoints(18);  // 设置高度多少磅（Point）

            // 设置水平对齐方式为居中对齐
            cellStyle.setAlignment(HorizontalAlignment.CENTER);
            // 设置垂直对齐方式为居中对齐
            cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);

            // 设置字体样式
            Font font = workbook.createFont();
            font.setFontName("微软雅黑");
            // font.setBold(true);
            font.setFontHeightInPoints((short) 10);
            // font.setColor(IndexedColors.WHITE.getIndex());
            cellStyle.setFont(font);

            cell.setCellStyle(cellStyle);
            if(Objects.nonNull(contentCellStyleConfig)) {
                contentCellStyleConfig.accept(context);
            }
            // 这里要把 WriteCellData的样式清空， 不然后面还有一个拦截器 FillStyleCellWriteHandler 默认会将 WriteCellStyle 设置到
            // cell里面去 会导致自己设置的不一样（很关键）
            context.getFirstCellData().setWriteCellStyle(null);
        }

    }



    /**
     * Excel 枚举值映射转换处理拦截器
     */
    public static class ExcelEnumWriteHandler implements CellWriteHandler {

        @Override
        public void afterCellDispose(CellWriteHandlerContext context) {
            // beforeCellCreate，afterCellCreate 对数据的修改会给后续逻辑覆盖
            // 参考：ExcelWriteAddExecutor.addJavaObjectToExcel、 AbstractExcelWriteExecutor.converterAndSet
            // 该方法在接口中最后执行，可对数据进行最终修改
            if (context.getHead() == null || context.getHead()) {
                // 只处理数据
                return;
            }

            ExcelContentProperty excelContentProperty = context.getExcelContentProperty();
            Field field = excelContentProperty.getField();
            // 以class固定列方式的才执行， map形式非class的动态类不执行
            if (Objects.nonNull(field)) {
                EnumView excelView = field.getAnnotation(EnumView.class);
                // 对ExcelView字段进行枚举值翻译
                if (Objects.nonNull(excelView)) {
                    // 传入的原始数据
                    Object originalValue = context.getOriginalValue();
                    if (Objects.nonNull(originalValue)) {
                        // 获取枚举映射map
                        Map<Object, String> enumConvertMap = this.getEnumConvertMap(field, excelView);
                        // 修改单元格数据
                        String newValue = enumConvertMap.get(originalValue);
                        if (Objects.nonNull(newValue)) {
                            context.getCell().setCellValue(newValue);
                        }
                    }
                }
            }
        }

        // 字段对应的枚举映射转换map 缓存
        private final Map<String, Map<Object, String>> fieldEnumConvertCacheMap = new HashMap<>();

        /**
         * 获取字段对应的枚举映射转换map
         * @param field
         * @param excelView
         * @return
         */
        private Map<Object, String> getEnumConvertMap(Field field, EnumView excelView) {
            // class模板类名
            String className = field.getDeclaringClass().getName();
            String cacheKey = className + ":" + field.getName();
            try {
                if (fieldEnumConvertCacheMap.containsKey(cacheKey)) {
                    return fieldEnumConvertCacheMap.get(cacheKey);
                }
                Map<Object, String> enumConvertMap = this.getEnumConvertMap(excelView);
                fieldEnumConvertCacheMap.put(cacheKey, enumConvertMap);
                return enumConvertMap;
            } catch (Exception e) {
                log.error("Excel导出-获取枚举转换异常 映射字段:{} 异常;{}", cacheKey, e.getMessage(), e);
                throw new RuntimeException(e);
            }
        }

        /**
         * 获取对应的枚举映射转换map
         * @param excelView
         * @return
         * @throws Exception
         */
        private Map<Object, String> getEnumConvertMap(EnumView excelView) throws Exception {
            Class<? extends Enum<?>> enumClass = excelView.value();
            Map<Object, String> result = new HashMap<>();
            Enum<?>[] enumConstants = enumClass.getEnumConstants();
            Field codeField = getField(enumClass, excelView.codeField());
            Field nameField = getField(enumClass, excelView.nameField());
            for (Enum<?> enumItem : enumConstants) {
                Object code = codeField.get(enumItem);;
                String name = (String) nameField.get(enumItem);;
                if (Objects.nonNull(code) && Objects.nonNull(name)) {
                    result.put(code, name);
                }
            }
            return result;
        }

        /**
         * 获取class字段
         * @param clazz
         * @param fieldName
         * @return
         */
        private Field getField(Class clazz, String fieldName){
            try {
                Field field =  clazz.getDeclaredField(fieldName);
                // 设置可访问性
                field.setAccessible(true);
                return field;
            } catch (NoSuchFieldException e) {
                throw new RuntimeException(clazz.getName() + "不存在字段" + fieldName);
            }
        }
    }

}
